package com.wky.sensitive.aspect;

import com.wky.sensitive.annotation.SensitiveReplace;
import com.wky.sensitive.constant.ThreadLocalConstant;
import com.wky.sensitive.enums.DataTypeEnum;
import com.wky.sensitive.rule.Rule;
import com.wky.sensitive.rule.abstracts.BeforeSensitiveProcessor;
import com.wky.sensitive.util.ReplaceUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

/**
 * 切面敏感出具处理类
 *
 * @author weikaiyu
 * @version 1.0
 * @Before("SensitivePointCut()")： before之前判断是否重写 BeforeSensitiveProcessor 处理器，
 * 将重写的方法返回值put到localVar中,调用isSensitive时即可判断，是否脱敏，若没重写默认是脱敏
 * @date 2022-02-10 16:28
 */
@Component
@Aspect
public class SensitiveCoreBean {


    private static ThreadLocal<Map<String, Object>> localVar = new ThreadLocal<>();


    public static ThreadLocal<Map<String, Object>> getLocalVar() {
        return localVar;
    }


    private static Logger logger = LoggerFactory.getLogger(SensitiveCoreBean.class);


    @Autowired
    List<BeforeSensitiveProcessor> beforeSensitiveProcessors;


    // 切入点 拦截 controller
    @Pointcut("@annotation(com.wky.sensitive.annotation.SensitiveReplace)")
    public void SensitivePointCut() {

    }


    /**
     * 请求到达之前
     *
     * @throws Exception
     */
    @Before("SensitivePointCut()")
    public void before() {
        // 设置是否脱敏
        if (null != beforeSensitiveProcessors) {
            // 接口的回调
            beforeSensitiveProcessors.stream().forEach(x -> {
                Map<String, Object> map = localVar.get();
                if (null == map) {
                    map = new HashMap<>();
                }
                map.put(ThreadLocalConstant.BOOL, x.bool());
                localVar.set(map);
            });
        }
    }

    /**
     * after调用 用于拦截Controller层数据处理脱敏信息
     *
     * @param joinPoint  切点
     * @param jsonResult 返回值
     * @see [类、类#方法、类#成员]
     */
    @AfterReturning(pointcut = "SensitivePointCut()", returning = "jsonResult")
    private void afterReturn(JoinPoint joinPoint, Object jsonResult) throws Exception {
        try {
            InnnerBean innnerBean = isSensitive(joinPoint);
            SensitiveReplace sensitiveReplace = innnerBean.sensitiveReplace;
            // 该用户没有配置脱敏角色,或者不存在注解，都不脱敏
            if (innnerBean.isFlag() == false || null == innnerBean.sensitiveReplace) {
                return;
            }
            //将 ThreadLocal 的值赋值给 jsonResult
            Map o = localVar.get();
            Object list = null;
            if (null != o) {
                list = o.get(ThreadLocalConstant.DATA);
            }
            if (list != null) {
                jsonResult = list;
            }
            if (null == o && jsonResult instanceof String) {
                String errMessage = "未赋予数据值，请使用SensitiveLocalUtil类的setLocalVar方法赋值";
                logger.error(errMessage);
                throw new Exception(errMessage);
            }
            // 脱敏处理
            filterSensitiveData(jsonResult, sensitiveReplace);
        } catch (Exception e) {
            logger.error("脱敏处理异常信息:{}", e.toString());
        }
    }

    /**
     * 脱敏处理
     *
     * @param jsonResult       要脱敏的数据
     * @param sensitiveReplace 注解信息
     */
    private void filterSensitiveData(Object jsonResult, SensitiveReplace sensitiveReplace) {


        DataTypeEnum dataTypeEnum = sensitiveReplace.dataType();

        // 集合
        if (Objects.equals(dataTypeEnum, DataTypeEnum.LIST)) {// 集合处理器
            listProcessor(jsonResult);
            return;
        }
        // 对象
        if (Objects.equals(dataTypeEnum, DataTypeEnum.ENTITY)) {
            entityProcessor(jsonResult);
            return;
        }

        //  统一返回体

        if (Objects.equals(dataTypeEnum, DataTypeEnum.COMMON)) {
            // 当类型为统一封装体时，不同用户可能有不一样的自定义返回体，需要用反射获取到值,目前支持获取一层
            Class<?> aClass = jsonResult.getClass();
            // 获取key值
            String key = sensitiveReplace.key();
            DataTypeEnum keyDataType = sensitiveReplace.keyDataType();
            String getter = "get" + ReplaceUtils.getMethodName(key);
            Method curMethod = null;
            try {
                curMethod = aClass.getMethod(getter);
            } catch (NoSuchMethodException e) {
                logger.error("找不到字段：{}的get方法", key);
            }
            // 从统一返回体get出来的值

            try {
                jsonResult = curMethod.invoke(jsonResult);
            } catch (IllegalAccessException e) {
                logger.error("数据：{}获取失败，注解@SensitiveReplace未赋予key属性值", key);
            } catch (InvocationTargetException e) {
                logger.error("数据：{}获取失败，注解@SensitiveReplace未赋予key属性值", key);
            }

            if (Objects.equals(keyDataType, DataTypeEnum.LIST)) {
                listProcessor(jsonResult);
                return;
            }
            // 对象
            if (Objects.equals(keyDataType, DataTypeEnum.ENTITY)) {
                entityProcessor(jsonResult);
                return;
            }
            return;
        }

    }

    /**
     * 实体类处理器
     *
     * @param jsonResult
     */
    private void entityProcessor(Object jsonResult) {
        Object object = jsonResult;
        Map<String, Object> replaceInfo = ReplaceUtils.getReplaceInfo(object);
        filterSensitive(object, replaceInfo);
    }

    /**
     * list处理器
     *
     * @param jsonResult
     */
    private void listProcessor(Object jsonResult) {
        List<Object> objectList;
        try {
            objectList = (List<Object>) jsonResult;
        } catch (ClassCastException e) {
            throw new ClassCastException("注解@SensitiveReplace的dataType值与数据值类型不匹配");
        }
        if (null == objectList || objectList.size() == 0) {
            return;
        }
        Map<String, Object> replaceInfo = ReplaceUtils.getReplaceInfo(objectList.get(0));
        filterSensitive(objectList, replaceInfo);
    }


    /**
     * 实体类方法重载
     *
     * @param dataTarget
     * @param hashMap
     */
    private static void filterSensitive(Object dataTarget, Map<String, Object> hashMap) {
        List<Object> dataSources = new ArrayList<>();
        dataSources.add(dataTarget);
        filterSensitive(dataSources, hashMap);
    }

    /**
     * 集合的重载方法
     * 将集合数据，map数据丢过来即可
     *
     * @param dataSources
     * @param hashMap
     */
    private static void filterSensitive(List<Object> dataSources, Map<String, Object> hashMap) {
        for (int j = 0; j < dataSources.size(); j++) {
            Set<Map.Entry<String, Object>> entries = hashMap.entrySet();
            Iterator<Map.Entry<String, Object>> iterator = entries.iterator();
            while (iterator.hasNext()) {
                Object object = dataSources.get(j);
                if (Objects.isNull(object)) break;
                Map.Entry<String, Object> next = iterator.next();
                String param = next.getKey();
                Object rulePath = next.getValue();
                String typeName = object.getClass().getTypeName();
                // List<String>类型，该类型只有一个属性，非实体类，不需要iterator.hasNext()迭代器循环，替换值后直接下一个 dataSources.get(j+1)
                if (Objects.equals(typeName, "java.lang.String")) {
                    dataSources.set(j, setTypeSensitive(rulePath, (String) object));
                    break;
                }
                // 获取属性名称，如果不存在则跳过，进行下一个属性值的替换
                try {
                    Field declaredField = object.getClass().getDeclaredField(param);
                    declaredField.setAccessible(true);
                    if (declaredField.getType().getName().equals("java.lang.String")) {
                        replace(param, object, rulePath);
                        continue;
                    }
                    if (declaredField.getType().getName().equals("java.util.List")) {

                        try {
                            List<Object> objectList = (List<Object>) declaredField.get(object);
                            if (null != objectList && objectList.size() >= 1) {
                                filterSensitive(objectList, hashMap);
                            }
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        }
                    }
                    // 如果不是集合，也不是字符串，则为实体,只处理这三种
                    else {
                        Object objects = null;
                        try {
                            objects = declaredField.get(object);
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        }
                        if (null != objects) {
                            filterSensitive(objects, hashMap);
                        }
                    }
                } catch (NoSuchFieldException e) {
                    // 找不到的属性，直接下一个属性的查找
                    continue;
                }
            }
        }
    }


    /**
     * 字符串的替换公共函数
     *
     * @param param
     * @param object
     * @param type
     * @return
     */
    private static void replace(String param, Object object, Object type) {
        // 对象的属性值
        String value = "";
        // 反射执行get方法：//get后首字母大写
        String getter = "get" + ReplaceUtils.getMethodName(param);
        Method curMethod = null;
        try {
            // 获取方法，判断是否是集合，还是属性字段
            curMethod = object.getClass().getMethod(getter);
            value = (String) curMethod.invoke(object);
        } catch (NoSuchMethodException e) {
            // 所有要脱敏的字段都放在一个map中（嵌套的实体标识有@Replace都存入map中），找不到这个方法，
            // 是因为该字段是嵌套实体类的属性，当前实体找不到方法这个方法即跳过循环下一个
            return;
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        // 根据脱敏类路径规则实现脱敏
        value = setTypeSensitive(type, value);
        // 反射执行set方法
        String setter = "set" + ReplaceUtils.getMethodName(param);
        try {
            // 将脱敏值设置回实体属性中
            Method curSetMethod = object.getClass().getMethod(setter, String.class);
            curSetMethod.invoke(object, value);
        } catch (NoSuchMethodException e) {
            logger.error("找不到属性:{}的set方法", param);
        } catch (InvocationTargetException e) {
            logger.error("属性:{}脱敏失败，异常：", e.getMessage());
        } catch (IllegalAccessException e) {
            logger.error("属性:{}脱敏失败，异常：", e.getMessage());
        }

    }

    /**
     * 根绝Replace注解上的rulePath类型，调用具体的实现类，实现敏感字符串的替换，
     *
     * @param rulePath
     * @param value
     * @return 替换后的字符串
     */
    private static String setTypeSensitive(Object rulePath, String value) {
        if (org.springframework.util.StringUtils.isEmpty(value)) {
            return "";
        }
        Class<Rule> aClass;
        // 根据类路径反射实例化对象
        aClass = (Class<Rule>) rulePath;
        try {
            Rule rule = aClass.newInstance();
            value = rule.processor(value);
        } catch (InstantiationException e) {
            logger.error("{}实" +
                    "例化失败，请重写Rule，异常：{}", rulePath, e.getMessage());
        } catch (IllegalAccessException e) {
            logger.error("{}实例化失败，请重写Rule，异常：{}", rulePath, e.getMessage());
        }
        return value;
    }


    /**
     * 判断 用户是否
     *
     * @param joinPoint 切点
     * @return true为脱敏，false为不脱敏
     * @throws Exception
     */
    @SuppressWarnings("rawtypes")
    private static InnnerBean isSensitive(JoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();
        Class<?>[] argTypes = new Class[joinPoint.getArgs().length];
        for (int i = 0; i < args.length; i++) {
            argTypes[i] = args[i].getClass();
        }
        Method method = null;
        try {
            method = joinPoint.getTarget().getClass()
                    .getDeclaredMethod(joinPoint.getSignature().getName(), argTypes);
            method.setAccessible(true);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        }
        SensitiveReplace annotation = method.getAnnotation(SensitiveReplace.class);
        // 获取注解
        if (null != annotation) {
            Map<String, Object> map = localVar.get();
            boolean bool = true;
            if (null != map) {
                Object o = map.get(ThreadLocalConstant.BOOL);
                if (o != null) {
                    bool = (boolean) o;
                }
            }
            return new InnnerBean(annotation, bool);
        }
        return new InnnerBean(null, false);
    }


    /**
     * 内部类封装注入信息
     *
     * @see [相关类/方法]
     * @since [产品/模块版本]
     */
    static class InnnerBean {
        private SensitiveReplace sensitiveReplace;// 模块代码

        private boolean flag = false; //默认不存在注解

        public InnnerBean(SensitiveReplace sensitiveReplace, boolean flag) {
            this.sensitiveReplace = sensitiveReplace;
            this.flag = flag;
        }

        public SensitiveReplace getSensitiveParam() {
            return sensitiveReplace;
        }

        public void setSensitiveParam(SensitiveReplace sensitiveReplace) {
            this.sensitiveReplace = sensitiveReplace;
        }

        public boolean isFlag() {
            return flag;
        }

        public void setFlag(boolean flag) {
            this.flag = flag;
        }
    }
}
